home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
276-300
/
disk_282
/
printhandler
/
print-handler.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
15KB
|
656 lines
/* Print-Handler.c v1.1, 30 September 1989
*
* This printer device handler implements the single sheet support
* and data-spooling facility the usual PRT: device does not have.
*
* Each time a page has been successfully transferred to the printer
* and single paper sheets are selected in Preferences, a requestor
* pops up on the Workbench screen telling the user to insert the
* next sheet of paper. If a process addresses this handler via
* Open() and Write()s data to it, the incoming text is buffered
* until the data stream is Close()d. After that the buffer sent to
* the printer device and finally gets discarded. Note that there
* will be a buffer for each calling process (up to twenty callers
* are allowed), so you may get low on memory. This handler will
* notice low memory situations and return a write error in critical
* situations. This handler does no first-in-first-out or last-in-
* first-out data stack handling, the first data to come out is from
* the first buffer to be closed
*
* Don't be confused if the requestor pops up while the printer is
* still working. Since most printers have a write buffer of their
* own true synchronous printer I/O is almost impossible.
*
* Installation is as follows:
*
* * Add the following lines to your DEVS:Mountlist
*
* PRT: Handler = L:Print-Handler
* Stacksize = 3000
* Priority = 5
* GlobVec = 1
* #
*
* * Add the following lines to your S:Startup-Sequence
*
* Assign PRT: Remove
* Mount PRT:
*
* * Copy Print-Handler to your L:
*
* Skeleton handler code by Phillip Lindsay (C) 1986 Commodore
* You may freely distribute this source and use it for Amiga Development,
* as long as the Copyright notice is left intact.
*
* Print-Handler (C) Copyright 1989 by Olaf Barthel & ED Hannover
*
* Contact: Olaf Barthel, Electronic Design Hannover
* Brabeckstrasse 35
* D-3000 Hannover 71
*
* Federal Republic of Germany
*/
#include <intuition/intuitionbase.h>
#include <libraries/filehandler.h>
#include <libraries/dosextens.h>
#include <devices/prtbase.h>
#include <devices/printer.h>
#include <exec/memory.h>
/* This version of BADDR() has no problems with castings. */
#undef BADDR
#define BADDR(x) ((APTR)((long)x << 2))
/* Define the packet types which are not available
* in the dos 1.1/1.2 include files.
*/
#define ACTION_FIND_INPUT 1005
#define ACTION_FIND_OUTPUT 1006
#define ACTION_END 1007
/* Some BCPL boolean definitions. */
#define DOS_FALSE 0
#define DOS_TRUE -1
/* Forward declarations. */
extern struct Library *OpenLibrary();
extern struct Process *FindTask();
extern struct MsgPort *CreatePort();
extern struct MsgPort *FindPort();
extern struct Message *GetMsg();
extern struct IOStdReq *CreateStdIO();
extern void *AllocMem();
/* This is a data segment, a part of a larger printer
* buffer used for data-spooling.
*/
struct DataSeg
{
struct DataSeg *NextSeg; /* Next segment. */
APTR Buffer; /* Data array. */
long Length; /* Length of array. */
};
/* Some global data. */
struct IntuitionBase *IntuitionBase;
struct Preferences *Preferences;
struct DataSeg *PrintSlot[20];
struct IOStdReq *PrinterDevice;
struct MsgPort *PrinterPort;
/* Position counters to take care of the current page length. */
long LinesDone = 0;
long ColumnsDone = 0;
/* CreateSeg(Buffer,Length):
*
* Creates a new segment entry for a linked list of
* buffers.
*/
struct DataSeg *
CreateSeg(Buffer,Length)
register APTR Buffer;
register long Length;
{
register struct DataSeg *NewSeg;
/* Allocate memory for the list element. */
if(NewSeg = (struct DataSeg *)AllocMem(sizeof(struct DataSeg),MEMF_PUBLIC | MEMF_CLEAR))
{
/* Allocate memory for the data buffer. */
if(NewSeg -> Buffer = (APTR)AllocMem(Length,MEMF_PUBLIC | MEMF_CLEAR))
{
/* Copy the data and set the buffer size. */
CopyMem(Buffer,NewSeg -> Buffer,Length);
NewSeg -> Length = Length;
/* Return the new segment. */
return(NewSeg);
}
/* Free the segment data. */
FreeMem(NewSeg,sizeof(struct DataSeg));
}
/* We failed. */
return(NULL);
}
/* DeleteSeg(OldSeg):
*
* Deletes both the contents and the memory occupied
* by a data segment and returns a pointer to the
* next segment.
*/
struct DataSeg *
DeleteSeg(OldSeg)
register struct DataSeg *OldSeg;
{
register struct DataSeg *NextSeg = NULL;
/* Valid pointer given? */
if(OldSeg)
{
/* Remember this. */
NextSeg = OldSeg -> NextSeg;
/* Free the contents of the buffer. */
if(OldSeg -> Buffer && OldSeg -> Length)
{
FreeMem(OldSeg -> Buffer,OldSeg -> Length);
/* Zero this out. */
OldSeg -> Buffer = NULL;
OldSeg -> Length = 0;
}
/* Free the segment data. */
FreeMem(OldSeg,sizeof(struct DataSeg));
}
/* Return pointer to next segment or null. */
return(NextSeg);
}
/* ReturnPacket(Packet,res1,res2):
*
* This one returns an AmigaDOS packet we have just
* received.
*/
void
ReturnPacket(Packet,res1,res2)
register struct DosPacket *Packet;
register ULONG res1,res2;
{
register struct Message *Message;
register struct MsgPort *ReplyPort;
register struct Process *ThisProg;
Packet -> dp_Res1 = res1;
Packet -> dp_Res2 = res2;
ReplyPort = Packet -> dp_Port;
Message = Packet -> dp_Link;
ThisProg = (struct Process *)FindTask(NULL);
Packet -> dp_Port = &ThisProg -> pr_MsgPort;
Message -> mn_Node . ln_Name = (char *)Packet;
Message -> mn_Node . ln_Succ = NULL;
Message -> mn_Node . ln_Pred = NULL;
PutMsg(ReplyPort,Message);
}
/* TaskWait():
*
* This one waits for an AmigaDOS packet to arrive
* and returns a pointer to it.
*/
struct DosPacket *
TaskWait()
{
register struct Process *ThisProg;
register struct MsgPort *MyPort;
register struct Message *MyMessage;
ThisProg = (struct Process *)FindTask(NULL);
MyPort = &ThisProg -> pr_MsgPort;
WaitPort(MyPort);
MyMessage = (struct Message *)GetMsg(MyPort);
return((struct DosPacket *)MyMessage -> mn_Node . ln_Name);
}
/* PrintIt(Buffer,Length):
*
* This function handles the printing. We could as well
* replace the data <-> printer transfer by a short call
* to Write(); probably the easiest way to implement
* output redirection.
*/
long
PrintIt(Buffer,Length)
register APTR Buffer;
register long Length;
{
/* Are buffer and size both valid? */
if(Buffer && Length)
{
/* Send the text to the printer. */
PrinterDevice -> io_Command = CMD_WRITE;
PrinterDevice -> io_Data = Buffer;
PrinterDevice -> io_Length = Length;
return(DoIO(PrinterDevice));
}
return(-1);
}
/* DoRequest():
*
* Displays the requestor asking to insert the next
* sheet of paper and returns the result.
*/
BOOL
DoRequest()
{
static struct TextAttr DefaultFont =
{
(UBYTE *)"topaz.font",9,FS_NORMAL,FPF_ROMFONT
};
static struct IntuiText RequestTxt[] =
{
{0,1,JAM1,18, 4,(struct TextAttr *)&DefaultFont,(UBYTE *)"Finished with current page,", &RequestTxt[1]},
{0,1,JAM1,18,14,(struct TextAttr *)&DefaultFont,(UBYTE *)"please insert next sheet.", NULL},
{0,1,JAM1, 5, 3,(struct TextAttr *)&DefaultFont,(UBYTE *)"Ready", NULL},
{0,1,JAM1, 5, 3,(struct TextAttr *)&DefaultFont,(UBYTE *)"Abort", NULL}
};
/* Open the Workbench first, we don't want to have a dead
* Workbench screen hanging around if a task preferred to
* close it.
*/
OpenWorkBench();
return(AutoRequest(NULL,&RequestTxt[0],&RequestTxt[2],&RequestTxt[3],NULL,NULL,326,59));
}
/* PrintData(Buffer,Length):
*
* This is the main interface processing the incoming
* data.
*/
BOOL
PrintData(Buffer,Length)
register char *Buffer;
register long Length;
{
register long i,j,InBuff = 0,PaperWidth = Preferences -> PrintRightMargin - Preferences -> PrintLeftMargin;
char LineStack[1024];
/* Clear out the contents of the line buffer. */
for(i = 0 ; i < 1024 ; i++)
LineStack[i] = 0;
if(Buffer)
{
for(i = 0 ; i < Length ; i++)
{
/* Put the data into the line buffer. */
LineStack[InBuff++] = Buffer[i];
ColumnsDone++;
/* Right margin reached/newline encountered? */
if(ColumnsDone == PaperWidth || Buffer[i] == '\n')
{
/* Overriding the paper width
* does not necessarily include a
* newline.
*/
if(ColumnsDone == PaperWidth || ColumnsDone == 1024)
{
LineStack[InBuff++] = '\n';
ColumnsDone++;
}
/* Send the line to the printer. */
PrintIt(LineStack,InBuff);
/* Did we reach the bottom line? */
if((++LinesDone) == Preferences -> PaperLength)
{
LinesDone = 0;
/* Inform the user about it. */
if(Preferences -> PaperType == SINGLE)
if(!DoRequest())
return(FALSE);
}
/* Clear the rest. */
ColumnsDone = InBuff = 0;
for(j = 0 ; j < 1024 ; j++)
LineStack[j] = 0;
}
}
/* Is there still something in the line buffer? */
if(InBuff)
PrintIt(LineStack,InBuff);
}
/* We succeeded and nobody cancelled us. */
return(TRUE);
}
/* _main():
*
* Handler main routine, bypasses all typical Aztec
* startup code.
*/
_main()
{
struct Process *ThisProg = (struct Process *)FindTask(NULL);
struct DosPacket *MyPacket;
struct DeviceNode *MyNode; /* Our device node passed in parmpkt Arg3. */
long OpenCount = 0; /* Handler open flag. */
BOOL Running = TRUE; /* Handler main loop flag. */
register long NextSlot = 0;
register long i;
/* The list of available buffers. */
UBYTE Available[20];
/* Since we were started as a non-BCPL module we get sent the
* parameter packet (i.e. parameter packet not in D1).
*/
MyPacket = TaskWait(); /* Wait for parameter packet. */
/* Mark all buffers as empty. */
for(i = 0 ; i < 20 ; i++)
{
Available[i] = TRUE;
PrintSlot[i] = NULL;
}
/* Get a pointer to our device node. */
MyNode = (struct DeviceNode *)BADDR(MyPacket -> dp_Arg3);
/* Do the main handler initialization. */
if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",33)))
{
ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
goto FallOff;
}
if(!(PrinterPort = (struct MsgPort *)CreatePort(NULL,0)))
{
ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
goto FallOff;
}
if(!(PrinterDevice = (struct IOStdReq *)CreateStdIO(PrinterPort)))
{
ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
goto FallOff;
}
if(OpenDevice("printer.device",0,PrinterDevice,0))
{
ReturnPacket(MyPacket,DOS_FALSE,MyPacket -> dp_Res2);
goto FallOff;
}
/* Initialize the Preferences pointer to
* reflect current printer.device settings.
*/
Preferences = &((struct PrinterData *)PrinterDevice -> io_Device) -> pd_Preferences;
/* If initialization was possible then we install our
* taskid. If we don't...for every reference to our
* handler a new process will be created. This is
* fine for things like CON: (console handler) but
* if you plan to be the only dude on block (like
* the file-system handler or SER:) you should fill
* the task field with your taskid (i.e.
* &(pr_MsgPort)) Note: remember that shared code
* has to be reentrant. (like CON: handler), keep
* your variables on the stack [autos], and allocate
* memory for larger data structures and "FLAG"
* global data structures that need only be
* intialized once.
*/
MyNode -> dn_Task = &ThisProg -> pr_MsgPort;
ReturnPacket(MyPacket,DOS_TRUE,MyPacket -> dp_Res2);
while(Running) /* Start of the real work. */
{
MyPacket = TaskWait(); /* Wait for a packet. */
switch(MyPacket -> dp_Type)
{
/* Somebody Open()ed us. */
case ACTION_FIND_INPUT:
case ACTION_FIND_OUTPUT:
{
struct FileHandle *FileHandle = (struct FileHandle *)BADDR(MyPacket -> dp_Arg1);
/* Assume failure. */
FileHandle -> fh_Port = DOS_FALSE;
for(i = 0 ; i < 20 ; i++)
{
/* Any buffer available? */
if(Available[i])
{
/* We didn't fail. */
FileHandle -> fh_Port = DOS_TRUE;
FileHandle -> fh_Arg1 = i;
Available[i] = FALSE;
/* Increment usercount. */
OpenCount++;
break;
}
}
/* Return the compliment. */
ReturnPacket(MyPacket,FileHandle -> fh_Port,MyPacket -> dp_Res2);
break;
}
/* Someone Close()d the file. */
case ACTION_END:
{
long TheSlotIs = MyPacket -> dp_Arg1;
BOOL GoOn = TRUE;
/* We want to fall out of the loop if not OPEN. */
if((--OpenCount) <= 0)
Running = FALSE;
ReturnPacket(MyPacket,DOS_TRUE,MyPacket -> dp_Res2);
/* Print the current buffer. */
while(PrintSlot[TheSlotIs])
{
if(GoOn)
if(!PrintData(PrintSlot[TheSlotIs] -> Buffer,PrintSlot[TheSlotIs] -> Length))
GoOn = FALSE;
PrintSlot[TheSlotIs] = DeleteSeg(PrintSlot[TheSlotIs]);
}
Available[TheSlotIs] = TRUE;
/* Reset the line counters. */
LinesDone = ColumnsDone = 0;
break;
}
/* Someone tries to Read() us. */
case ACTION_READ:
{
/* We *always* read nothing. */
ReturnPacket(MyPacket,NULL,MyPacket -> dp_Res2);
break;
}
/* Someone tries to Write() to us. */
case ACTION_WRITE:
{
long TheSlotIs = MyPacket -> dp_Arg1;
char *Buffer = (char *)MyPacket -> dp_Arg2;
struct DataSeg *NextSlot;
MyPacket -> dp_Res1 = MyPacket -> dp_Arg3;
/* Buffer not initialized yet? */
if(!PrintSlot[TheSlotIs])
{
if(!(PrintSlot[TheSlotIs] = CreateSeg(Buffer,MyPacket -> dp_Res1)))
MyPacket -> dp_Res1 = -1;
}
else
{
/* Add a new buffer to the current list. */
NextSlot = PrintSlot[TheSlotIs];
while(NextSlot -> NextSeg)
NextSlot = NextSlot -> NextSeg;
if(!(NextSlot -> NextSeg = CreateSeg(Buffer,MyPacket -> dp_Res1)))
MyPacket -> dp_Res1 = -1;
}
/* We *always* write everything. */
ReturnPacket(MyPacket,MyPacket -> dp_Arg3,MyPacket -> dp_Res2);
break;
}
/* Someone wants us the leave the town. */
case ACTION_DIE:
{
/* Empty all buffers. */
for(i = 0 ; i < 20 ; i++)
while(PrintSlot[i])
PrintSlot[i] = DeleteSeg(PrintSlot[i]);
/* Result will be ignored anyway. */
ReturnPacket(MyPacket,DOS_FALSE,ERROR_ACTION_NOT_KNOWN);
goto FallOff;
}
/* For any other purpose: ignore the message. */
default:
{
/* Say what? */
ReturnPacket(MyPacket,DOS_FALSE,ERROR_ACTION_NOT_KNOWN);
break;
}
}
}
/* Fall off the edge of the world. */
FallOff:MyNode -> dn_Task = FALSE; /* Zero the TaskID field of device node. */
/* Free all our data. */
if(PrinterDevice)
{
if(PrinterDevice -> io_Device)
CloseDevice(PrinterDevice);
DeleteStdIO(PrinterDevice);
}
if(PrinterPort)
DeletePort(PrinterPort);
if(IntuitionBase)
CloseLibrary(IntuitionBase);
/* This is truly the end. */
}